﻿/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#include "../include/Live2dModel.hpp"
#include <fstream>
#include <vector>
#include <CubismModelSettingJson.hpp>
#include <Motion/CubismMotion.hpp>
#include <Physics/CubismPhysics.hpp>
#include <CubismDefaultParameterId.hpp>
#include <Rendering/OpenGL/CubismRenderer_OpenGLES2.hpp>
#include <Utils/CubismString.hpp>
#include <Id/CubismIdManager.hpp>
#include <Motion/CubismMotionQueueEntry.hpp>
#include "../include/Live2dDefine.hpp"
#include "../include/Live2dPal.hpp"
#include "../include/Live2dTextureManager.hpp"
#include "../include/Live2dDelegate.hpp"


using namespace Live2D::Cubism::Framework;
using namespace Live2D::Cubism::Framework::DefaultParameterId;
using namespace Live2dDefine;

namespace {
    csmByte* CreateBuffer(const csmChar* path, csmSizeInt* size)
    {
        if (DebugLogEnable)
        {
            Live2dPal::PrintLog("create buffer: %s ", path);
        }
        return Live2dPal::LoadFileAsBytes(path, size);
    }

    void DeleteBuffer(csmByte* buffer, const csmChar* path = "")
    {
        if (DebugLogEnable)
        {
            Live2dPal::PrintLog("delete buffer: %s", path);
        }
        Live2dPal::ReleaseBytes(buffer);
    }
}

Live2dModel::Live2dModel()
    : CubismUserModel()
    , _modelSetting(NULL)
    , _userTimeSeconds(0.0f)
    , mouse_left(false)
    , mouse_right(false)
    , left_hand_down(false)
    , right_hand_down(false)
    , stick_left_down(false)
    , stick_right_down(false)
    , stick_show_left_hand(false)
    , stick_show_right_hand(false)
    , stick_l_x(0.0f)
    , stick_l_y(0.0f)
    , stick_r_x(0.0f)
    , stick_r_y(0.0f)
    , speed(1.0f)
    ,randomMotion(false)
    , randomExpression(false)
{
    if (DebugLogEnable)
    {
        _debugMode = true;
    }

    _idParamAngleX = CubismFramework::GetIdManager()->GetId(ParamAngleX);
    _idParamAngleY = CubismFramework::GetIdManager()->GetId(ParamAngleY);
    _idParamAngleZ = CubismFramework::GetIdManager()->GetId(ParamAngleZ);
    _idParamBodyAngleX = CubismFramework::GetIdManager()->GetId(ParamBodyAngleX);
    _idParamEyeBallX = CubismFramework::GetIdManager()->GetId(ParamEyeBallX);
    _idParamEyeBallY = CubismFramework::GetIdManager()->GetId(ParamEyeBallY);

    _idParamMouseX = CubismFramework::GetIdManager()->GetId(ParaMouseX);
    _idParamMouseY = CubismFramework::GetIdManager()->GetId(ParaMouseY);
    _idParamLeftButton = CubismFramework::GetIdManager()->GetId(ParaLeftButton);
    _idParamRightButton = CubismFramework::GetIdManager()->GetId(ParaRightButton);
    _idParamLeftHandDown = CubismFramework::GetIdManager()->GetId(ParaLeftHandDown);
    _idParamRightHandDown = CubismFramework::GetIdManager()->GetId(ParaRightHandDown);

    _idParamStickLeftDown = CubismFramework::GetIdManager()->GetId(ParaStickLeftDown);
    _idParamStickRightDown = CubismFramework::GetIdManager()->GetId(ParaStickRightDown);
    _idParamStickShowLeftHand = CubismFramework::GetIdManager()->GetId(ParaStickShowLeftHand);
    _idParamStickShowRightHand = CubismFramework::GetIdManager()->GetId(ParaStickShowRightHand);
    _idParamStickLX = CubismFramework::GetIdManager()->GetId(ParaStickLX);
    _idParamStickLY = CubismFramework::GetIdManager()->GetId(ParaStickLY);
    _idParamStickRX = CubismFramework::GetIdManager()->GetId(ParaStickRX);
    _idParamStickRY = CubismFramework::GetIdManager()->GetId(ParaStickRY);
}

Live2dModel::~Live2dModel()
{
    _renderBuffer.DestroyOffscreenFrame();

    ReleaseMotions();
    ReleaseExpressions();

    for (csmInt32 i = 0; i < _modelSetting->GetMotionGroupCount(); i++)
    {
        const csmChar* group = _modelSetting->GetMotionGroupName(i);
        ReleaseMotionGroup(group);
    }
    delete(_modelSetting);
}

void Live2dModel::LoadAssets(const csmChar* dir, const csmChar* fileName)
{
    _modelHomeDir = dir;

    if (_debugMode)
    {
        Live2dPal::PrintLog("load model setting: %s", fileName);
    }

    csmSizeInt size;
    const csmString path = csmString(dir) + fileName;

    csmByte* buffer = CreateBuffer(path.GetRawString(), &size);
    ICubismModelSetting* setting = new CubismModelSettingJson(buffer, size);
    DeleteBuffer(buffer, path.GetRawString());

    SetupModel(setting);

    CreateRenderer();

    SetupTextures();
}

void Live2dModel::SetupModel(ICubismModelSetting* setting)
{
    _updating = true;
    _initialized = false;

    _modelSetting = setting;

    csmByte* buffer;
    csmSizeInt size;

    //Cubism Model
    if (strcmp(_modelSetting->GetModelFileName(), "") != 0)
    {
        csmString path = _modelSetting->GetModelFileName();
        path = _modelHomeDir + path;

        if (_debugMode)
        {
            Live2dPal::PrintLog("create model: %s", setting->GetModelFileName());
        }

        buffer = CreateBuffer(path.GetRawString(), &size);
        LoadModel(buffer, size);
        DeleteBuffer(buffer, path.GetRawString());
    }

    //Expression
    if (_modelSetting->GetExpressionCount() > 0)
    {
        const csmInt32 count = _modelSetting->GetExpressionCount();
        for (csmInt32 i = 0; i < count; i++)
        {
            csmString name = _modelSetting->GetExpressionName(i);
            csmString path = _modelSetting->GetExpressionFileName(i);
            path = _modelHomeDir + path;

            buffer = CreateBuffer(path.GetRawString(), &size);
            ACubismMotion* motion = LoadExpression(buffer, size, name.GetRawString());

            if (_expressions[name] != NULL)
            {
                ACubismMotion::Delete(_expressions[name]);
                _expressions[name] = NULL;
            }
            _expressions[name] = motion;

            DeleteBuffer(buffer, path.GetRawString());
        }
    }

    //Physics
    if (strcmp(_modelSetting->GetPhysicsFileName(), "") != 0)
    {
        csmString path = _modelSetting->GetPhysicsFileName();
        path = _modelHomeDir + path;

        buffer = CreateBuffer(path.GetRawString(), &size);
        LoadPhysics(buffer, size);
        DeleteBuffer(buffer, path.GetRawString());
    }

    //Pose
    if (strcmp(_modelSetting->GetPoseFileName(), "") != 0)
    {
        csmString path = _modelSetting->GetPoseFileName();
        path = _modelHomeDir + path;

        buffer = CreateBuffer(path.GetRawString(), &size);
        LoadPose(buffer, size);
        DeleteBuffer(buffer, path.GetRawString());
    }

    //EyeBlink
    if (_modelSetting->GetEyeBlinkParameterCount() > 0)
    {
        _eyeBlink = CubismEyeBlink::Create(_modelSetting);
    }

    //Breath
    {
        _breath = CubismBreath::Create();

        csmVector<CubismBreath::BreathParameterData> breathParameters;

        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleX, 0.0f, 15.0f, 6.5345f, 0.5f));
        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleY, 0.0f, 8.0f, 3.5345f, 0.5f));
        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamAngleZ, 0.0f, 10.0f, 5.5345f, 0.5f));
        breathParameters.PushBack(CubismBreath::BreathParameterData(_idParamBodyAngleX, 0.0f, 4.0f, 15.5345f, 0.5f));
        breathParameters.PushBack(CubismBreath::BreathParameterData(CubismFramework::GetIdManager()->GetId(ParamBreath), 0.5f, 0.5f, 3.2345f, 0.5f));

        _breath->SetParameters(breathParameters);
    }

    //UserData
    if (strcmp(_modelSetting->GetUserDataFile(), "") != 0)
    {
        csmString path = _modelSetting->GetUserDataFile();
        path = _modelHomeDir + path;
        buffer = CreateBuffer(path.GetRawString(), &size);
        LoadUserData(buffer, size);
        DeleteBuffer(buffer, path.GetRawString());
    }

    // EyeBlinkIds
    {
        csmInt32 eyeBlinkIdCount = _modelSetting->GetEyeBlinkParameterCount();
        for (csmInt32 i = 0; i < eyeBlinkIdCount; ++i)
        {
            _eyeBlinkIds.PushBack(_modelSetting->GetEyeBlinkParameterId(i));
        }
    }

    // LipSyncIds
    {
        csmInt32 lipSyncIdCount = _modelSetting->GetLipSyncParameterCount();
        for (csmInt32 i = 0; i < lipSyncIdCount; ++i)
        {
            _lipSyncIds.PushBack(_modelSetting->GetLipSyncParameterId(i));
        }
    }

    //Layout
    csmMap<csmString, csmFloat32> layout;
    _modelSetting->GetLayoutMap(layout);
    _modelMatrix->SetupFromLayout(layout);
    
    _model->SaveParameters();

    for (csmInt32 i = 0; i < _modelSetting->GetMotionGroupCount(); i++)
    {
        const csmChar* group = _modelSetting->GetMotionGroupName(i);
        PreloadMotionGroup(group);
    }

    _motionManager->StopAllMotions();

    _updating = false;
    _initialized = true;
}

void Live2dModel::PreloadMotionGroup(const csmChar* group)
{
    const csmInt32 count = _modelSetting->GetMotionCount(group);

    for (csmInt32 i = 0; i < count; i++)
    {
        //ex) idle_0
        csmString name = Utils::CubismString::GetFormatedString("%s_%d", group, i);
        csmString path = _modelSetting->GetMotionFileName(group, i);
        path = _modelHomeDir + path;

        if (_debugMode)
        {
            Live2dPal::PrintLog("load motion: %s => [%s_%d] ", path.GetRawString(), group, i);
        }

        csmByte* buffer;
        csmSizeInt size;
        buffer = CreateBuffer(path.GetRawString(), &size);
        CubismMotion* tmpMotion = static_cast<CubismMotion*>(LoadMotion(buffer, size, name.GetRawString()));

        csmFloat32 fadeTime = _modelSetting->GetMotionFadeInTimeValue(group, i);
        if (fadeTime >= 0.0f)
        {
            tmpMotion->SetFadeInTime(fadeTime);
        }

        fadeTime = _modelSetting->GetMotionFadeOutTimeValue(group, i);
        if (fadeTime >= 0.0f)
        {
            tmpMotion->SetFadeOutTime(fadeTime);
        }
        tmpMotion->SetEffectIds(_eyeBlinkIds, _lipSyncIds);

        if (_motions[name] != NULL)
        {
            ACubismMotion::Delete(_motions[name]);
        }
        _motions[name] = tmpMotion;

        DeleteBuffer(buffer, path.GetRawString());
    }
}

void Live2dModel::OnMouseClick(const Csm::csmInt32 key, const Csm::csmBool key_up)
{
    switch (key)
    {
    case MOUSE_LEFT:
        key_up ? mouse_left=false:mouse_left=true;
        break;
    case MOUSE_RIGHT:
        key_up ? mouse_right = false : mouse_right = true;
         break;
    default:
        break;
    }
}

void Live2dModel::OnLeftHandDown(const Csm::csmBool key_up)
{
    key_up ? left_hand_down=false: left_hand_down =true;
}

void Live2dModel::OnRightHandDown(const Csm::csmBool key_up)
{
    key_up ? right_hand_down = false : right_hand_down = true;
}

void Live2dModel::OnStickLeftDown(const Csm::csmBool key_up)
{
    key_up ? stick_left_down = false : stick_left_down = true;
}

void Live2dModel::OnStickRightDown(const Csm::csmBool key_up)
{
    key_up ? stick_right_down = false : stick_right_down = true;
}

void Live2dModel::OnStickShowLeftHand(const Csm::csmBool key_up)
{
    key_up ? stick_show_left_hand = false : stick_show_left_hand = true;
}

void Live2dModel::OnStickShowRightHand(const Csm::csmBool key_up)
{
    key_up ? stick_show_right_hand = false : stick_show_right_hand = true;
}

void Live2dModel::OnStickL(const Csm::csmFloat32 x, const Csm::csmFloat32 y)
{
    stick_l_x = x;
    stick_l_y = y;
}

void Live2dModel::OnStickR(const Csm::csmFloat32 x, const Csm::csmFloat32 y)
{
    stick_r_x = x;
    stick_r_y = y;
}

void Live2dModel::SetSpeed(const Csm::csmFloat32 _speed)
{
    speed = _speed;
}

void Live2dModel::RandomMotion(const Csm::csmBool _random)
{
    randomMotion = _random;
}

void Live2dModel::RandomExpression(const Csm::csmBool _random)
{
    randomExpression = _random;
}

void Live2dModel::ReleaseMotionGroup(const csmChar* group) const
{
    const csmInt32 count = _modelSetting->GetMotionCount(group);
    for (csmInt32 i = 0; i < count; i++)
    {
        csmString voice = _modelSetting->GetMotionSoundFileName(group, i);
        if (strcmp(voice.GetRawString(), "") != 0)
        {
            csmString path = voice;
            path = _modelHomeDir + path;
        }
    }
}

/**
* @brief すべてのモーションデータの解放
*
* すべてのモーションデータを解放する。
*/
void Live2dModel::ReleaseMotions()
{
    for (csmMap<csmString, ACubismMotion*>::const_iterator iter = _motions.Begin(); iter != _motions.End(); ++iter)
    {
        ACubismMotion::Delete(iter->Second);
    }

    _motions.Clear();
}

/**
* @brief すべての表情データの解放
*
* すべての表情データを解放する。
*/
void Live2dModel::ReleaseExpressions()
{
    for (csmMap<csmString, ACubismMotion*>::const_iterator iter = _expressions.Begin(); iter != _expressions.End(); ++iter)
    {
        ACubismMotion::Delete(iter->Second);
    }

    _expressions.Clear();
}

void Live2dModel::Update()
{
    const csmFloat32 deltaTimeSeconds = speed*Live2dPal::GetDeltaTime();
    _userTimeSeconds += deltaTimeSeconds;

    _dragManager->Update(deltaTimeSeconds);
    _dragX = _dragManager->GetX();
    _dragY = _dragManager->GetY();

    // モーションによるパラメータ更新の有無
    csmBool motionUpdated = false;

    //-----------------------------------------------------------------
    _model->LoadParameters(); // 前回セーブされた状態をロード
   
    if (_motionManager->IsFinished())
    {
        if (randomMotion) {  

            if (randomExpression)
                SetRandomExpression();

            if (_modelSetting->GetMotionGroupCount() > 0) {
                csmInt32 nog = rand() % _modelSetting->GetMotionGroupCount();
                const csmChar* group = _modelSetting->GetMotionGroupName(nog);

                if (group&&_modelSetting->GetMotionCount(group) != 0)
                {
                    StartRandomMotion(group, PriorityIdle);
                }
            }              
        }
        // モーションの再生がない場合、待機モーションの中からランダムで再生する
        //StartRandomMotion("CAT_motion_lock", PriorityIdle);
        //motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds);
    }
    else
    {
        motionUpdated = _motionManager->UpdateMotion(_model, deltaTimeSeconds); // モーションを更新
    }
    

    _model->SaveParameters(); // 状態を保存
    //-----------------------------------------------------------------
    
    // まばたき
    if (!motionUpdated)
    {
        if (_eyeBlink != NULL)
        {
            // メインモーションの更新がないとき
            _eyeBlink->UpdateParameters(_model, deltaTimeSeconds); // 目パチ
        }
    }

    if (_expressionManager != NULL)
    {
        if (_expressionManager->IsFinished())
        {
            
        }
        else
        {
            _expressionManager->UpdateMotion(_model, deltaTimeSeconds); // 表情でパラメータ更新（相対変化）
        }        
    }

    //ドラッグによる変化
    //ドラッグによる顔の向きの調整
    _model->AddParameterValue(_idParamAngleX, _dragX * 60 - 30); // -30から30の値を加える
    _model->AddParameterValue(_idParamAngleY, _dragY * 60 - 30);
    _model->AddParameterValue(_idParamAngleZ, _dragX * _dragY * 60 - 30);

    //ドラッグによる体の向きの調整
    _model->AddParameterValue(_idParamBodyAngleX, _dragX * 10); // -10から10の値を加える

    //ドラッグによる目の向きの調整
    _model->AddParameterValue(_idParamEyeBallX, _dragX * 60 - 30); // -1から1の値を加える
    _model->AddParameterValue(_idParamEyeBallY, _dragY * 60 - 30);

    /************************Mouse & keyboard********************************/
    _model->AddParameterValue(_idParamMouseX, _dragX * 60 - 30);
    _model->AddParameterValue(_idParamMouseY, _dragY * 60 - 30);

    _model->AddParameterValue(_idParamLeftButton, mouse_left? 1.0f : 0.0f);
    _model->AddParameterValue(_idParamRightButton, mouse_right ? 1.0f : 0.0f);

    _model->AddParameterValue(_idParamLeftHandDown, left_hand_down ? 1.0f : 0.0f);
    _model->AddParameterValue(_idParamRightHandDown, right_hand_down ? 1.0f : 0.0f);

    /************************JoyStick********************************/
    _model->AddParameterValue(_idParamStickLeftDown, stick_left_down ? 1.0f : 0.0f);
    _model->AddParameterValue(_idParamStickRightDown, stick_right_down ? 1.0f : 0.0f);

    _model->AddParameterValue(_idParamStickShowLeftHand, stick_show_left_hand ? 1.0f : 0.0f);
    _model->AddParameterValue(_idParamStickShowRightHand, stick_show_right_hand ? 1.0f : 0.0f);

    _model->AddParameterValue(_idParamStickLX, stick_l_x * 30);
    _model->AddParameterValue(_idParamStickLY, stick_l_y * 30);

    _model->AddParameterValue(_idParamStickRX, stick_r_x * 30);
    _model->AddParameterValue(_idParamStickRY, stick_r_y * 30);
    /****************************************************************/
    // 呼吸など
    if (_breath != NULL)
    {
        _breath->UpdateParameters(_model, deltaTimeSeconds);
    }

    // 物理演算の設定
    if (_physics != NULL)
    {
        _physics->Evaluate(_model, deltaTimeSeconds);
    }

    // リップシンクの設定
    if (_lipSync)
    {
        csmFloat32 value = 0; // リアルタイムでリップシンクを行う場合、システムから音量を取得して0〜1の範囲で値を入力します。

        for (csmUint32 i = 0; i < _lipSyncIds.GetSize(); ++i)
        {
            _model->AddParameterValue(_lipSyncIds[i], value, 0.8f);
        }
    }

    // ポーズの設定
    if (_pose != NULL)
    {
        _pose->UpdateParameters(_model, deltaTimeSeconds);
    }

    _model->Update();

}

CubismMotionQueueEntryHandle Live2dModel::StartMotion(const csmChar* group, csmInt32 no, csmInt32 priority, ACubismMotion::FinishedMotionCallback onFinishedMotionHandler)
{
    if (priority == PriorityForce)
    {
        _motionManager->SetReservePriority(priority);
    }
    else if (!_motionManager->ReserveMotion(priority))
    {
        if (_debugMode)
        {
            Live2dPal::PrintLog("can't start motion.");
        }
        return InvalidMotionQueueEntryHandleValue;
    }

    const csmString fileName = _modelSetting->GetMotionFileName(group, no);

    if (fileName.GetLength()==0)
        return InvalidMotionQueueEntryHandleValue;

    //ex) idle_0
    csmString name = Utils::CubismString::GetFormatedString("%s_%d", group, no);
    CubismMotion* motion = static_cast<CubismMotion*>(_motions[name.GetRawString()]);
    csmBool autoDelete = false;

    if (motion == NULL)
    {
        csmString path = fileName;
        path = _modelHomeDir + path;

        csmByte* buffer;
        csmSizeInt size;
        buffer = CreateBuffer(path.GetRawString(), &size);
        motion = static_cast<CubismMotion*>(LoadMotion(buffer, size, NULL, onFinishedMotionHandler));
        csmFloat32 fadeTime = _modelSetting->GetMotionFadeInTimeValue(group, no);
        if (fadeTime >= 0.0f)
        {
            motion->SetFadeInTime(fadeTime);
        }

        fadeTime = _modelSetting->GetMotionFadeOutTimeValue(group, no);
        if (fadeTime >= 0.0f)
        {
            motion->SetFadeOutTime(fadeTime);
        }
        motion->SetEffectIds(_eyeBlinkIds, _lipSyncIds);
        autoDelete = true; // 終了時にメモリから削除

        DeleteBuffer(buffer, path.GetRawString());
    }
    else
    {
        motion->SetFinishedMotionHandler(onFinishedMotionHandler);
    }

    //voice
    csmString voice = _modelSetting->GetMotionSoundFileName(group, no);
    if (strcmp(voice.GetRawString(), "") != 0)
    {
        csmString path = voice;
        path = _modelHomeDir + path;
        cat_play_sound(path.GetRawString(),false);
    }

    if (_debugMode)
    {
        Live2dPal::PrintLog("start motion: [%s_%d]", group, no);
    }
    return  _motionManager->StartMotionPriority(motion, autoDelete, priority);
}

CubismMotionQueueEntryHandle Live2dModel::StartRandomMotion(const csmChar* group, csmInt32 priority, ACubismMotion::FinishedMotionCallback onFinishedMotionHandler)
{
    if (_modelSetting->GetMotionCount(group) == 0)
    {
        return InvalidMotionQueueEntryHandleValue;
    }
         
    csmInt32 no = rand() % _modelSetting->GetMotionCount(group);

    return StartMotion(group, no, priority, onFinishedMotionHandler);
}

Csm::CubismMotionQueueEntryHandle Live2dModel::StartMotion(const Csm::csmChar* group, const Csm::csmInt32 id, Csm::ACubismMotion::FinishedMotionCallback onFinishedMotionHandler)
{

    if (!_motionManager->IsFinished())
    {
        return InvalidMotionQueueEntryHandleValue;
    }
    
    csmInt32 no =  _modelSetting->GetMotionCount(group);
    
    if (id<0 || id>no)
    {
        return InvalidMotionQueueEntryHandleValue;
    }

    return StartMotion(group, id, PriorityForce, onFinishedMotionHandler);
}

void Live2dModel::DoDraw()
{
    if (_model == NULL)
    {
        return;
    }

    GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->DrawModel();
}

void Live2dModel::Draw(CubismMatrix44& matrix)
{
    if (_model == NULL)
    {
        return;
    }

    matrix.MultiplyByMatrix(_modelMatrix);

    GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->SetMvpMatrix(&matrix);

    DoDraw();
}

void Live2dModel::SetExpression(const csmChar* expressionID)
{
    ACubismMotion* motion = _expressions[expressionID];
    if (_debugMode)
    {
        Live2dPal::PrintLog("expression: [%s]", expressionID);
    }

    if (motion != NULL)
    {
        _expressionManager->StartMotionPriority(motion, false, PriorityForce);
    }
    else
    {
        if (_debugMode) Live2dPal::PrintLog("expression[%s] is null ", expressionID);
    }
}

void Live2dModel::SetExpressionByID(const Csm::csmInt32 expressionID)
{
    csmMap<csmString, ACubismMotion*>::const_iterator map_ite;
    csmInt32 i = 0;
    for (map_ite = _expressions.Begin(); map_ite != _expressions.End(); map_ite++)
    {
        if (i == expressionID)
        {
            csmString name = (*map_ite).First;
            SetExpression(name.GetRawString());
            return;
        }
        i++;
    }
}

void Live2dModel::SetRandomExpression()
{
    if (_expressions.GetSize() == 0)
    {
        return;
    }

    csmInt32 no = rand() % _expressions.GetSize();
    csmMap<csmString, ACubismMotion*>::const_iterator map_ite;
    csmInt32 i = 0;
    for (map_ite = _expressions.Begin(); map_ite != _expressions.End(); map_ite++)
    {
        if (i == no)
        {
            csmString name = (*map_ite).First;
            SetExpression(name.GetRawString());
            return;
        }
        i++;
    }
}

void Live2dModel::ReloadRenderer()
{
    DeleteRenderer();

    CreateRenderer();

    SetupTextures();
}

void Live2dModel::SetupTextures()
{
    for (csmInt32 modelTextureNumber = 0; modelTextureNumber < _modelSetting->GetTextureCount(); modelTextureNumber++)
    {
        // テクスチャ名が空文字だった場合はロード・バインド処理をスキップ
        if (strcmp(_modelSetting->GetTextureFileName(modelTextureNumber), "") == 0)
        {
            continue;
        }

        //OpenGLのテクスチャユニットにテクスチャをロードする
        csmString texturePath = _modelSetting->GetTextureFileName(modelTextureNumber);
        texturePath = _modelHomeDir + texturePath;

        Live2dTextureManager::TextureInfo* texture = Live2dDelegate::GetInstance()->GetTextureManager()->CreateTextureFromPngFile(texturePath.GetRawString());
        const csmInt32 glTextueNumber = texture->id;

        //OpenGL
        GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->BindTexture(modelTextureNumber, glTextueNumber);
    }

#ifdef PREMULTIPLIED_ALPHA_ENABLE
    GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->IsPremultipliedAlpha(true);
#else
    GetRenderer<Rendering::CubismRenderer_OpenGLES2>()->IsPremultipliedAlpha(false);
#endif

}

void Live2dModel::MotionEventFired(const csmString& eventValue)
{
    CubismLogInfo("%s is fired on LAppModel!!", eventValue.GetRawString());
}

Csm::Rendering::CubismOffscreenFrame_OpenGLES2& Live2dModel::GetRenderBuffer()
{
    return _renderBuffer;
}
